﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Translate;
using System.Windows.Forms;
using System.Text.RegularExpressions;
using System.Web;
using Google.API.Translate;
using System.Net;
using System.IO;
using System.Collections.Specialized;
using TextTools;
using System.Threading.Tasks;
using System.Diagnostics;

namespace AlexTranslator
{
    public class TranslationTools
    {

        public static bool SplitByNewLines = true;

        public static string MakeReplacingForYandex(string src, bool forward)
        {
            src = MakeReplacingForPere(src, forward);

            
            List<ReplacingPair> array = new List<ReplacingPair>();
            array.Add(new ReplacingPair("|", "{sl} "));
            array.Add(new BackReplacingPair( "\\\"", "\"" ));
            array.Add(new BackReplacingPair(" .", "."));
            return MakeReplacing(src, array, forward);
        }

        public static string MakeReplacingForPere(string src, bool forward)
        {
            List<ReplacingPair> array = new List<ReplacingPair>();            
            array.Add(new ReplacingPair("|", " | "));
            array.Add(new ReplacingPair("\n", " <n> ", "<n> ", false));
            array.Add(new ReplacingPair("\n", "<n>", false));
            array.Add(new ReplacingPair("[[", "[[ "));
            //array.Add(new ReplacingPair("]]", " ]]")); // next pair will substitute it
            array.Add(new ReplacingPair("]", " ]"));
            array.Add(new ReplacingPair(".", " . "));
            array.Add(new ReplacingPair("=", " = "));
            array.Add(new ReplacingPair(",", " , "));
            array.Add(new ReplacingPair("'''", "<trp>"));
            array.Add(new ReplacingPair("'''", "<dbl>"));
            array.Add(new ReplacingPair("атегорія:", "атегорія :", true));
            array.Add(new ReplacingPair("атегорія: ", "атегорія:", true));
            
            array.Add(new WordsDividerFile());
            return MakeReplacing(src, array, forward);
        }

        public static string MakeReplacingForGoogle(string src, bool forward)
        {
            if(forward)
                src = src.Replace(Convert.ToChar(160), Convert.ToChar(32));

            List<ReplacingPair> array = new List<ReplacingPair>();
            array.Add(new ReplacingPair("{{", "<fb>")); //first bracket
            array.Add(new ReplacingPair("}}", "<lb>")); // last bracket
            //array.Add(new ReplacingPair("'''", "<trpl>"));
            //array.Add(new ReplacingPair("''", "=''=", "=' '=", false));
            /*
            array.Add(new ReplacingPair("\r\n", " [rn] "));            
            
            array.Add(new ReplacingPair("\n", "<n>"));
            array.Add(new ReplacingPair("\n", "<n> "));
            array.Add(new ReplacingPair("\n", "< n>"));
            array.Add(new ReplacingPair("\n", "<n >"));
            array.Add(new ReplacingPair("\n", "< n> "));
            array.Add(new ReplacingPair("\n", "<n > "));
             */ 
            array.Add(new ReplacingPair("|-", "<t->"));
            array.Add(new ReplacingPair("||", "<| |>"));
            array.Add(new ReplacingPair("{|", "<(|>"));
            array.Add(new ReplacingPair("|}", "<|)>"));
            array.Add(new ReplacingPair("http://", "Http://", true));
            array.Add(new ReplacingPair("/~", "/--")); // for links
            array.Add(new ReplacingPair("]]", "] ]", true));
            array.Add(new ReplacingPair("[[", "[ [", true));
            array.Add(new BackReplacingPair( ". Svg", ".svg"));// . Svg
            array.Add(new BackReplacingPair(". Jpg", ".jpg" ));
            array.Add(new TranslationPair("до н. э.", "<dne>", "до н. е."));
            array.Add(new TranslationPair("до н.э.", "<dne>", "до н. е."));
            array.Add(new TranslationPair("н.э.", "<n_e>", "н. е."));
            array.Add(new TranslationPair("н. э.", "<n_e>", "н. е."));
            array.Add(new BackReplacingPair("имсько-католи", "имо-католи"));
            array.Add(new BackReplacingPair("| ", "|"));
            array.Add(new BackReplacingPair("« ", "«"));
            array.Add(new BackReplacingPair(" »", "»"));
            array.Add(new BackReplacingPair("\n ", "\n"));
            array.Add(new BackReplacingPair("</ ref>", "</ref>"));
            array.Add(new BackReplacingPair("' '", "''"));
            
            array.Add(new ReplacingPair("Артёмовский район Донецкой области]]", "Артёмовский район (Донецкая область)]]"));
            array.Add(new ReplacingPair("Славянский район Донецкой области]]", "Славянский район]]"));
            array.Add(new ReplacingPair("[[Категория:Амвросиевский район Донецкой области]]", "[[Категория:Амвросиевский район]]"));
            
            /*array.Add(new WordsDivider("семитск",false));
            array.Add(new WordsDivider("аккадск", false));
            array.Add(new WordsDivider("акадск", false));
            array.Add(new WordsDivider("вавилонск", false));
            array.Add(new WordsDivider("ассирийск", false));
             */
            array.Add(new WordsDividerFile()); //Please remember to take the file with you.

            return MakeReplacing(src, array, forward);
        }

        public static string MakeReplacing(string src, 
            List<ReplacingPair> array, bool forward)
        {

            //Parallel.ForEach(array, (pair) => src = pair.Replace(src, forward)); //not suitable, because it does not preserve order
              
            foreach (ReplacingPair pair in array)
            {
                src = pair.Replace(src, forward);
            }
              
            if (forward && false)
            {

                try
                {
                    if (Regex.IsMatch(src, @"\b(')[\s,.-]")) //regex works wrong on cyrillic text
                    {
                        //errorProvider1.SetError(rbDst, "found several right ' characters, and replaced them to ='");
                        src = Regex.Replace(src, @"\b(')[\s,.-]", "=$&");
                    }
                }
                catch (ArgumentException )
                {
                    // Syntax error in the regular expression
                }
            }
            return src;
        }

        public static List<string> SplitToPartsGoogle(string text)
        {
            int minLength = 370 - 1;
            int maxLength = 1940; //for get

            minLength = 4000; // 2000 did not helped
             maxLength = 8000; //for post //post does not work. It seems it is done through get.


             List<string> result = null;
             StopwatchExtensions.BenchMark(() =>
                result = SplitToParts(text, minLength, maxLength), 
             "google split");
            return result;
        }

        public static List<string> SplitToParts(string text, int minLength, int maxLength)
        {
            //Regex regexObj = new Regex(@"([^|\?\!\r\n\ .,]*)[|\?\!\r\n\ .,]");
            Regex regexObj;

            if(SplitByNewLines)
                regexObj = new Regex(@"([^\r\n]*)[\r\n]"); // for lines only
            else
                regexObj = new Regex(@"([^|\?\!\r\n\ .,]*)[|\?\!\r\n\ .,]");

            //text = text + " a";// we will remove it after splitting. This is because regexp can sse so the end .

            var matches = regexObj.Matches(text);
            List<string> result = new List<string>();
            string portion = "";
            int startIndex = 0;


            for (int i = 0; i < matches.Count; i++)
            {
                Match word = matches[i];
                
                //if (matches[i].Index + matches[i].Length < startIndex + maxLength) //the same but less readable
                //if (word.Index + word.Length > startIndex + maxLength)
                if (portion.Length + word.Length > minLength)
                    if (HttpUtility.UrlEncode(portion + word.Value).Length > maxLength)
                    {
                        result.Add(portion);
                        if(portion.Length> maxLength)
                            MessageBox.Show("Test");
                        portion = "";
                        startIndex = word.Index;
                        //i--;// just to go to true and add the portion
                    }
                portion += word.Value ;
            }
            result.Add(text.Substring(startIndex));
            //result.Add(portion.Remove(portion.Length-1));//removing added '.'

            string concat = null;
            foreach (string block in result)
            {
                concat += block;
            }
            if (concat != text)
                MessageBox.Show("the splitted text!=source text");

            

            Logger.Write(string.Format("Text size:{1}. Text splitted to {0} sections", result.Count, text.Length));
            Logger.Write(string.Format("Old splitting mekhanism would split it to {0} sections", (text.Length / minLength) + 1));
            return result;
        }


        public static string WikiGoogleTranslate(string text, string from, string to, bool insertBlockMarker)
        {
            //https://translate.yandex.ru/tr.json/translate?lang=en-ru&text=YourText

            Dictionary<string,string> refs = new Dictionary<string,string>();
            //text = TextTools.ReplaceRefs(text, true, refs);

            text = MakeReplacingForGoogle(text, true);
            string gooResult="";
            try
            {
                StopwatchExtensions.BenchMark(() =>
                gooResult = GoogleTranslate(text, from, to, insertBlockMarker), "WikiGoogleTranslate");
            }
            catch
            {
                throw;
            }
            gooResult = MakeReplacingForGoogle(gooResult, false);
            //gooResult = TextTools.ReplaceRefs(gooResult, false, refs);

            if(gooResult.Contains("<n"))
                MessageBox.Show("Something wrong. The replacement markup('<n>') is present in the text.");

            return gooResult;
        }

        public static string GoogleTranslate(string text, string from, string to)
        {
            //Result res = new Result();
            return GoogleTranslate(text, from, to, false);
        }

        public static string blockMarker = "☻";//☻ - marker of the end of block
        public static string GoogleTranslate(string text, string from, string to, bool insertBlockMarker)
        {
            Google.API.Translate.TranslateClient client 
                = new TranslateClient(@"Alex Blokha wiki markup translator for ukrainian language");
                //long text in referer is not included in URL
                //= new TranslateClient(@"3902http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/http://code.google.com/p/google-api-for-dotnet/");
                                            //http://translate.google.ru/translate_a/t?client=x&text={0}&sl={1}&tl={2}

            
            List<string> parts = SplitToPartsGoogle(text);

            string result = "";
            string translatedBlock = "";            
            

            List<int> positions = new List<int>();
            
            string block = "";
            try
            {
                for (int i = 0; i < parts.Count; i++)
                {
                    block = parts[i];
                    /*
                    if (from == Google.API.Translate.Language.Unknown)
                    {                        
                        translatedBlock = client.TranslateAndDetect(
                    block, to, TranslateFormat.Text, out from);
                    }
                    else */
                    {
                        translatedBlock = GoogleHack.GetTranslationByPost(block,from,to);
                        //translatedBlock = GoogleHack.GetTranslationByGet(block);

                        //translatedBlock = client.Translate(
                        //block, from, to, TranslateFormat.Text);
                    }

                    string breakerToAdd;

                    if (SplitByNewLines)
                    {
                        breakerToAdd = "\n";
                    }
                    else
                    {
                        breakerToAdd = " ";
                    }

                    result += breakerToAdd + translatedBlock + (insertBlockMarker ? blockMarker : "");

                    positions.Add(result.Length);

                    
                }
                result = result.Trim();
            }
            catch( Exception ex)
            {
                MessageBox.Show(ex.ToString());
                throw;
            }
            return result;
        }

        public static string YandexTranslate(string text, string langFrom, string LangTo)
        {
            List<string> parts = SplitToParts(text,4000,8000);

            string result="";
            for (int i = 0; i < parts.Count(); i++)
            {
                result += YandexTranslatePost(parts[i], langFrom, LangTo);
            }
            result = result.Trim('"');
            return result;
            
        }

        public static string YandexTranslatePost(string text, string langFrom, string LangTo)
        {
            PostSubmitter post = new PostSubmitter();
            post.Url = "https://translate.yandex.ru/tr.json/translate";
            post.PostItems.Add("lang", langFrom+"-"+LangTo); //"ru-uk"
            post.PostItems.Add("text", text);            
            post.Type = PostSubmitter.PostTypeEnum.Post;
            string result = post.Post();

            return result;
        }


        public static string YandexTranslateGet(string text)
        {
            string sURL;
            sURL = 
                "https://translate.yandex.ru/tr.json/translate?lang=ru-uk&text=";

            sURL += HttpUtility.UrlEncode(text);

            WebRequest wrGETURL;
            wrGETURL = WebRequest.Create(sURL);

            //WebProxy myProxy = new WebProxy("myproxy", 80);
            //myProxy.BypassProxyOnLocal = true;
            //wrGETURL.Proxy = WebProxy.GetDefaultProxy();

            Stream objStream;
            objStream = wrGETURL.GetResponse().GetResponseStream();

            string result;
            using (StreamReader objReader = new StreamReader(objStream))
            {
                result = objReader.ReadToEnd();
            }

            return result;
        }

        public static string WikiYandexTranslate(string src)
        {
            src = MakeReplacingForYandex(src, true);
            string pereklad = YandexTranslate(src, "ru", "uk");
            pereklad = MakeReplacingForYandex(pereklad, false);

            return pereklad;
        }

        public static string WikiPereTranslate(string src, LanguagePair lngPair)
        {

            //return WikiYandexTranslate(src);

            string pereResult = "";
            //BackgroundWorker bg = new BackgroundWorker();
            //bg.DoWork += delegate(object sender, DoWorkEventArgs e)
            //{
            PereOrgUATranslator trans = new PereOrgUATranslator();

            var pResult = trans.Translate(MakeReplacingForPere(src, true),
                lngPair,
                "Common", new NetworkSetting());

            if (pResult.Translations.Count > 0)
            {
                pereResult = pResult.Translations[0];
                pereResult = MakeReplacingForPere(pereResult, false);
            }
            else
                pereResult = "Translation failed. It seems server is not responding.";
            return pereResult;
        }
    }

    public static class StopwatchExtensions
    {
        public static long BenchMark(this Stopwatch sw, Action action, string message)
        {
            sw.Reset();
            sw.Start();

            action();

            sw.Stop();
            Console.WriteLine(message + " occured in sec:" + sw.Elapsed.TotalSeconds);

            return sw.ElapsedMilliseconds;
        }

        public static long BenchMark(Action action, string message)
        {
            Stopwatch sw = new Stopwatch();
            sw.Reset();
            sw.Start();

            action();

            sw.Stop();
            Console.WriteLine(message + " occured in sec:" + sw.Elapsed.TotalSeconds);

            return sw.ElapsedMilliseconds;
        }
    }

}
